前面應該看到蠻多使用ScriptableObject的例子,現在終於要認真介紹一下這到底是幹什麼了
ScriptableObject是Unity提供讓程式的數據可以直接存在資料夾的方式,尤其要是今天該數據擁有預設值(ex:職業基本數值)那他的實用性就會大幅上升,下面先展示程式並解釋一些Unity本身擁有的功能,Odin的內容那些下篇就會說明這邊先跳過
UnitData.cs
using LinXuan.TBSF.Units;
using Sirenix.OdinInspector;
using System.Collections.Generic;
using TbsFramework.Cells;
using TbsFramework.Units;
using UnityEngine;
using Sirenix.OdinInspector;
namespace LinXuan.TBSF.Data
{
[CreateAssetMenu(fileName = "UnitData", menuName = "SaveData/UnitData")]
[InlineEditor]
public sealed class UnitData : ScriptableObject
{
[BoxGroup("Basic Info")]
[SerializeField] private string m_UnitName;
[HorizontalGroup("GameDataGroup")]
[PreviewField(75)]
[HideLabel]
[SerializeField] private UnitView m_UnitView;
[VerticalGroup("GameDataGroup/States")]
[SerializeField] private int m_MaxHp;
[VerticalGroup("GameDataGroup/States")]
[SerializeField] private int m_UnitNumber;
[VerticalGroup("GameDataGroup/States")]
[SerializeField] private int m_MaxStamina;
[VerticalGroup("GameDataGroup/States")]
[SerializeField] private int m_MaxMovementTimes;
[VerticalGroup("GameDataGroup/States")]
[SerializeField] private int m_MaxActionTimes;
[VerticalGroup("GameDataGroup/States")]
[SerializeField] private int m_AttackPoint;
[VerticalGroup("GameDataGroup/States")]
[LabelWidth(70)]
[Range(0, 10)]
[SerializeField] private int m_AttackRange;
[VerticalGroup("GameDataGroup/States")]
[SerializeField] private int m_MoveRange;
private int m_CurrentStamina;
private int m_CurrentMovementTimes;
private int m_CurrentActionTimes;
private List<(Buff buff, int timeLeft)> Buffs;
public int CurrentStamina { get => m_CurrentStamina; set { Mathf.Clamp(value, 0, m_MaxStamina); } }
public UnitView UnitView { get => m_UnitView; private set => m_UnitView = value; }
public int PlayerNumber;
public bool Obstructable = true;
public string UnitName { get => m_UnitName; set => m_UnitName = value; }
[SerializeField]
[HideInInspector]
private Cell cell;
public Cell Cell
{
get
{
return cell;
}
set
{
cell = value;
}
}
public void Initialize()
{
Buffs = new List<(Buff, int)>();
CurrentStamina = m_MaxStamina;
}
}
}
上面程式在Inspector會長這樣
CreateAssetMenu
可以用捷徑的方式獲取ScriptableObject,以[CreateAssetMenu(fileName = "UnitData", menuName = "SaveData/UnitData")]
這段程式來說,當你右鍵並展開Create時,可以看到此路徑
最後可以產生出這類物件,每個產生的UnitData都是獨立的物件,可以擁有各自的數值跟資料名稱
不過要是直接使用Asset裡面的ScriptableObject的資料,那要是在遊戲裡資料有所更改,那會一起更動,要注意,需要用那個實體化的指令才能new出一個新的,範例可以看前面的Factory模式(二)那篇
如果今天參數是public
或SerializeField
化的資料,可以透過[HideInInspector]
這指令讓此參數不會顯示在Inspector中
sealed 用來防止繼承,用在ScriptableObject這種純粹存資料的地方特別適合。但也因此基本上我只會建議資料類型的class用sealed,因為基本上這杜絕了任何擴充的可能,違反了OCP原則